#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <uxr/client/client.h>
#include <arpa/inet.h>
#include "Student.h"  // 包含生成的XRCE-DDS序列化代码
#include <ucdr/microcdr.h>  // Include Micro XRCE-DDS serialization library

#define STREAM_HISTORY 8
#define BUFFER_SIZE UXR_CONFIG_UDP_TRANSPORT_MTU * STREAM_HISTORY

#define FILENAME_BUFFER_SIZE 1024

void receive_filename(int udp_socket) {
    char filename_buffer[FILENAME_BUFFER_SIZE];
    struct sockaddr_in server_addr;
    socklen_t addr_len = sizeof(server_addr);

    int len = recvfrom(udp_socket, filename_buffer, sizeof(filename_buffer) - 1, 0, (struct sockaddr *)&server_addr, &addr_len);
    if (len > 0) {
        filename_buffer[len] = '\0';
        printf("Received filename: %s\n", filename_buffer);
    } else {
        printf("No filename received or error occurred.\n");
    }
}

int main(int argc, char** argv) {
    // CLI
    if (argc < 3 || 0 == atoi(argv[2])) {
        printf("usage: program [-h | --help] | ip port [<max_topics>]\n");
        return 0;
    }

    char* ip = argv[1];
    char* port = argv[2];
    uint32_t max_topics = (argc == 4) ? (uint32_t)atoi(argv[3]) : UINT32_MAX;

    // Transport
    uxrUDPTransport transport;
    if (!uxr_init_udp_transport(&transport, UXR_IPv4, ip, port)) {
        printf("Error at create transport.\n");
        return 1;
    }

    // Session
    uxrSession session;
    uxr_init_session(&session, &transport.comm, 0xAAAABBBB);
    if (!uxr_create_session(&session)) {
        printf("Error at create session.\n");
        uxr_close_udp_transport(&transport);
        return 1;
    }

    // Streams
    uint8_t output_reliable_stream_buffer[BUFFER_SIZE];
    uxrStreamId reliable_out = uxr_create_output_reliable_stream(&session, output_reliable_stream_buffer, BUFFER_SIZE, STREAM_HISTORY);

    uint8_t input_reliable_stream_buffer[BUFFER_SIZE];
    uxrStreamId reliable_in = uxr_create_input_reliable_stream(&session, input_reliable_stream_buffer, BUFFER_SIZE, STREAM_HISTORY);

    // Create entities
    uxrObjectId participant_id = uxr_object_id(0x01, UXR_PARTICIPANT_ID);
    const char* participant_xml = "<dds>"
                                  "<participant>"
                                  "<rtps>"
                                  "<name>default_xrce_participant</name>"
                                  "</rtps>"
                                  "</participant>"
                                  "</dds>";
    uint16_t participant_req = uxr_buffer_create_participant_xml(&session, reliable_out, participant_id, 0, participant_xml, UXR_REPLACE);

    uxrObjectId topic_id = uxr_object_id(0x01, UXR_TOPIC_ID);
    const char* topic_xml = "<dds>"
                            "<topic>"
                            "<name>student_topic</name>"
                            "<dataType>student</dataType>"
                            "</topic>"
                            "</dds>";
    uint16_t topic_req = uxr_buffer_create_topic_xml(&session, reliable_out, topic_id, participant_id, topic_xml, UXR_REPLACE);

    uxrObjectId publisher_id = uxr_object_id(0x01, UXR_PUBLISHER_ID);
    const char* publisher_xml = "<dds>"
                                "<publisher>"
                                "<name>default_publisher</name>"
                                "</publisher>"
                                "</dds>";
    uint16_t publisher_req = uxr_buffer_create_publisher_xml(&session, reliable_out, publisher_id, participant_id, publisher_xml, UXR_REPLACE);

    uxrObjectId datawriter_id = uxr_object_id(0x01, UXR_DATAWRITER_ID);
    const char* datawriter_xml = "<dds>"
                                 "<data_writer>"
                                 "<topic>"
                                 "<kind>NO_KEY</kind>"
                                 "<name>student_topic</name>"
                                 "<dataType>student</dataType>"
                                 "</topic>"
                                 "</data_writer>"
                                 "</dds>";
    uint16_t datawriter_req = uxr_buffer_create_datawriter_xml(&session, reliable_out, datawriter_id, publisher_id, datawriter_xml, UXR_REPLACE);

    // Create subscriber and datareader for receiving data
    uxrObjectId subscriber_id = uxr_object_id(0x01, UXR_SUBSCRIBER_ID);
    const char* subscriber_xml = "<dds>"
                                 "<subscriber>"
                                 "<name>default_subscriber</name>"
                                 "</subscriber>"
                                 "</dds>";
    uint16_t subscriber_req = uxr_buffer_create_subscriber_xml(&session, reliable_out, subscriber_id, participant_id, subscriber_xml, UXR_REPLACE);

    uxrObjectId datareader_id = uxr_object_id(0x01, UXR_DATAREADER_ID);
    const char* datareader_xml = "<dds>"
                                 "<data_reader>"
                                 "<topic>"
                                 "<kind>NO_KEY</kind>"
                                 "<name>student_topic</name>"
                                 "<dataType>student</dataType>"
                                 "</topic>"
                                 "</data_reader>"
                                 "</dds>";
    uint16_t datareader_req = uxr_buffer_create_datareader_xml(&session, reliable_out, datareader_id, subscriber_id, datareader_xml, UXR_REPLACE);

    // Send create entities message and wait for their status
    uint8_t status[6];
    uint16_t requests[6] = { participant_req, topic_req, publisher_req, datawriter_req, subscriber_req, datareader_req };
    if (!uxr_run_session_until_all_status(&session, 1000, requests, status, 6)) {
        printf("Error at creating entities: participant: %i topic: %i publisher: %i datawriter: %i subscriber: %i datareader: %i\n", 
                status[0], status[1], status[2], status[3], status[4], status[5]);
        uxr_delete_session(&session);
        uxr_close_udp_transport(&transport);
        return 1;
    }

    // Create a separate socket for receiving filenames
    int udp_socket;
    if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("Error at create UDP socket.\n");
        return 1;
    }

    struct sockaddr_in client_addr;
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(2010);  // 服务端接收文件名的端口
    inet_pton(AF_INET, ip, &client_addr.sin_addr);

    if (bind(udp_socket, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) {
        printf("Error at bind UDP socket.\n");
        close(udp_socket);
        return 1;
    }

    // Write and receive topics
    bool connected = true;
    uint32_t count = 0;
    srand(time(NULL));
    while (connected && count < max_topics) {
        // Send student data
        student s;
        snprintf(s.name, sizeof(s.name), "student%d", rand() % 100000);
        s.number = rand() % 100000;
        s.grade = 1;
        snprintf(s.hobby[0], sizeof(s.hobby[0]), "hobby1");
        snprintf(s.hobby[1], sizeof(s.hobby[1]), "hobby2");
        snprintf(s.hobby[2], sizeof(s.hobby[2]), "hobby3");

        ucdrBuffer mb;
        uint32_t topic_size = student_size_of_topic(&s, 0);
        uxr_prepare_output_stream(&session, reliable_out, datawriter_id, &mb, topic_size);

        if (!student_serialize_topic(&mb, &s)) {
            printf("Serialization error\n");
            break;
        }

        printf("Send student: %s, number: %ld, grade: %ld, hobby1: %s, hobby2: %s, hobby3: %s\n", 
                s.name, s.number, s.grade, s.hobby[0], s.hobby[1], s.hobby[2]);

        // Print serialized data
        printf("Serialized data: ");
        uint32_t length = ucdr_buffer_length(&mb);
        for (size_t i = 0; i < length; ++i) {
            printf("%02x ", *((uint8_t*)mb.init + i));
        }
        printf("\n");

        // Process received data
        connected = uxr_run_session_time(&session, 1000);
        while (uxr_get_topic_data_length(&session, datareader_id) > 0) {
            ucdrBuffer data_ub;
            uxr_get_topic_data(&session, datareader_id, &data_ub);
        }

        // Receive filename from server
        receive_filename(udp_socket);

        count++;
        usleep(100000);  // Sleep for 100 milliseconds
    }

    // Delete resources
    uxr_delete_session(&session);
    uxr_close_udp_transport(&transport);
    close(udp_socket);

    return 0;
}
